#include <Arduino.h>
#include "ptime.h"
#include "config.h"
#include "plog.h"

static const long gmtOffset_sec = 8 * 3600; // 时区偏移量，北京是GMT+8
static const int daylightOffset_sec = 0;    // 夏令时偏移量，中国无夏令时
// 连接wifi之后调用
void sync_sys_time()
{
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServerUrl3, ntpServerUrl2, ntpServerUrl1);

  LOG_INFO("Waiting for NTP local time sync: ");
  time_t nowSecs = time(nullptr);
  while (nowSecs < 8 * 3600 * 2)
  {
    delay(500);
    Serial.print(F("."));
    yield();
    nowSecs = time(nullptr);
  }

  Serial.println();
  print_local_time();
}

void print_local_time()
{
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo))
  {
    LOG_ERROR("Failed to obtain time");
    return;
  }
  Serial.printf("Current Local time: %s",asctime(&timeinfo));
  //  Serial.println(&timeinfo, "%F %T %A");
}

// Create a DS1302 object.
DS1302 rtc(KCE_PIN, KIO_PIN, KSCK_PIN);

void sync_to_ds1302(Time *t)
{
  // Initialize a new chip by turning off write protection and clearing the
  // clock halt flag. These methods needn't always be called. See the DS1302
  // datasheet for details.
  rtc.writeProtect(false);
  rtc.halt(false);

  LOG_DEBUG("sync to ds1302: %s", format_ds1302_time(t));

  // Set the time and date on the chip.
  rtc.time(*t);

  print_ds1302_time();
}

void sync_sys_time_to_ds1302(void)
{
  // Make a new time object to set the date and time.
  // Sunday, September 22, 2013 at 01:38:50.
  // Time t(2013, 9, 22, 1, 38, 50, Time::kSunday);
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo))
  {
    LOG_ERROR("Failed to sync time to ds1302");
    return;
  }

  // 将tm_wday(0=周日)转换为Time::Day(1=周日)
  Time::Day day_of_week = static_cast<Time::Day>(timeinfo.tm_wday + 1);
  Time t = Time(timeinfo.tm_year + 1900,
                timeinfo.tm_mon + 1,
                timeinfo.tm_mday,
                timeinfo.tm_hour,
                timeinfo.tm_min,
                timeinfo.tm_sec,
                day_of_week);
  sync_to_ds1302(&t);
}

Time get_time_from_ds1302(void)
{
  return rtc.time();
}

void ds1302_test()
{
  Time t(2025,
         1,
         24,
         9,
         45,
         31,
         Time::kMonday);
  sync_to_ds1302(&t);

  // sync_sys_time_to_ds1302();
  print_ds1302_time();
}

Timestamp_t timestamp_from_ds1302(void)
{
  Time t = get_time_from_ds1302();
  return get_timestamp(&t);
}
// 方案	      精度	年份范围	占用字节	适合场景
// mktime	    1秒	1901-2038	  4	    短期项目（2038年前）
// 自定义秒/2	  2秒	2000-2099	  4	  长期中等精度记录
// 分钟级	    1分钟	2000-2099	  4	  超长期低频记录

// 方案1
// uint32_t timestamp_from_ds1302(void)
// {
// Time t = rtc.time();
//   struct tm tm = {
//     .tm_sec = t.sec,
//     .tm_min = t.min,
//     .tm_hour = t.hr,
//     .tm_mday = t.date,
//     .tm_mon = t.mon - 1,  // tm_mon: 0-11
//     .tm_year = t.yr - 1900 // tm_year: 年份-1900
//   };
//   return (uint32_t)mktime(&tm); // 可能丢失2038年后数据
// }

// 方案2，因为是每分钟采集数据，所以采用
Timestamp_t get_timestamp(Time *t)
{
  /* 位域分配（32位）：
     | 31-25 | 24-21 | 20-16 | 15-11 | 10-5  | 4-0   |
     | 年份7  | 月份4  | 日期5  | 小时5  | 分钟6  | 秒/2 5  |
     - 年份: 2000-2099 → 0-99 (7位)
     - 秒数: 除2后存0-29 (5位)，精度降为2秒
  */
  return ((t->yr - 2000) << 25) |
         ((t->mon & 0x0F) << 21) |  // 月份限制4位
         ((t->date & 0x1F) << 16) | // 日期限制5位
         ((t->hr & 0x1F) << 11) |   // 小时限制5位
         ((t->min & 0x3F) << 5) |   // 分钟限制6位
         ((t->sec / 2) & 0x1F);     // 秒/2限制5位
}

// 方案3
// uint32_t getDS1302MinutesTimestamp() {
//   Time t = rtc.time();

//   /* 位域分配：
//      | 31-25 | 24-20 | 19-16 | 15-11 | 10-0 |
//      | 年份  | 月份  | 日期  | 小时  | 分钟 |
//   */
//   return ((t.yr - 2000) << 25) |
//          (t.mon << 20) |
//          (t.date << 16) |
//          (t.hr << 11) |
//          t.min;
// }

Time parse_timestamp(Timestamp_t ts)
{
  Time t(((ts >> 25) & 0x7F) + 2000,
         (ts >> 21) & 0x0F,
         (ts >> 16) & 0x1F,
         (ts >> 11) & 0x1F,
         (ts >> 5) & 0x3F,
         (ts & 0x1F) * 2,  // 恢复秒数近似值
         Time::kSaturday); // 星期几不重要
  return t;
}

char *timestamp_key(Timestamp_t ts)
{
  static char key[9] = {};
  memset(key, 0, 9);
  sprintf(key, "%08x", ts);
  return key;
}

String dayAsString(const Time::Day day)
{
  switch (day)
  {
  case Time::kSunday:
    return "Sunday";
  case Time::kMonday:
    return "Monday";
  case Time::kTuesday:
    return "Tuesday";
  case Time::kWednesday:
    return "Wednesday";
  case Time::kThursday:
    return "Thursday";
  case Time::kFriday:
    return "Friday";
  case Time::kSaturday:
    return "Saturday";
  }
  return "(unknown day)";
}

void print_ds1302_time(void)
{
  // Get the current time and date from the chip.
  Time t = rtc.time();

  // Print the formatted string to serial so we can see the time.
  LOG_INFO("Current ds1302 time: %s", format_ds1302_time(&t));
}

char *format_ds1302_time(Time *t)
{
  // Name the day of the week.
  const String day = dayAsString(t->day);

  // Format the time and date and insert into the temporary buffer.
  static char buf[100];
  snprintf(buf, sizeof(buf), "%s %04d-%02d-%02d %02d:%02d:%02d",
           day.c_str(),
           t->yr, t->mon, t->date,
           t->hr, t->min, t->sec);
  return buf;
}

char *format_ds1302_time_no_wday(Time *t)
{
  // Format the time and date and insert into the temporary buffer.
  static char buf_no_wday[100];
  snprintf(buf_no_wday, sizeof(buf_no_wday), "%04d-%02d-%02d %02d:%02d:%02d",
           t->yr, t->mon, t->date,
           t->hr, t->min, t->sec);
  return buf_no_wday;
}

char *format_ds1302_timestamp_no_wday(Timestamp_t timestamp)
{
  Time t = parse_timestamp(timestamp);
  return format_ds1302_time_no_wday(&t);
}

void timestamp_test()
{
  Time t(2025,
         1,
         24,
         9,
         45,
         31,
         Time::kMonday);
  Timestamp_t ts = get_timestamp(&t);
  LOG_INFO("raw ts: %ld, %08x", ts, ts);

  Time t2 = parse_timestamp(ts);
  LOG_INFO("parse ts: %s", format_ds1302_time(&t2));
}

